﻿// Sigma ASC 105 LED Panel Control Library - http://www.codeplex.com/SigmaASC105lib/
// Copyright (c) Michal A. Valasek - Altairis, 2008 - http://www.altairis.cz/
// This code is licensed under terms of the Microsoft Public License (Ms-PL).

using System;
using System.IO.Ports;
using System.Text;

namespace Altairis.LedPanelControl {
    /// <summary>
    /// This class communicates with the LED panel via serial port
    /// </summary>
    public class ControlClient : IDisposable {
        // Default serial port settings
        private const int DefaultBaudRate = 2400;
        private const Parity DefaultParity = Parity.None;
        private const int DefaultDataBits = 8;
        private const StopBits DefaultStopBits = StopBits.One;

        private SerialPort port;

        /// <summary>
        /// Initializes a new instance of the <see cref="ControlClient"/> class with default communication options.
        /// </summary>
        /// <param name="portName">Name of the serial port.</param>
        public ControlClient(string portName)
            : this(portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="ControlClient"/> class and specifies the advanced communication options.
        /// </summary>
        /// <param name="portName">Name of the serial port.</param>
        /// <param name="baudRate">The baud rate.</param>
        /// <param name="parity">The parity.</param>
        /// <param name="dataBits">The data bits.</param>
        /// <param name="stopBits">The stop bits.</param>
        /// <remarks>
        /// Please note that you probably woult not need to set the advanced options like baud rate etc. 
        /// The defaults for other constructor overload are valid and tested for the Sigma ASC 105 panel.
        /// </remarks>
        public ControlClient(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) {
            this.port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
        }

        /// <summary>
        /// Sends message to first memory of the LCD panel.
        /// </summary>
        /// <param name="message">The message to be sent.</param>
        public void SendMessage(Message message) {
            SendMessage(message, 1);
        }

        /// <summary>
        /// Sends message to the LCD panel.
        /// </summary>
        /// <param name="message">The message to be sent.</param>
        /// <param name="memory">Number of memory (1-99).</param>
        public void SendMessage(Message message, int memory) {
            if (this.disposed) throw new ObjectDisposedException("ControlClient");

            // Validate arguments
            if (message == null) throw new ArgumentNullException("message");
            if (memory < 1 || memory > 99) throw new ArgumentOutOfRangeException("memory", memory, "Memory must be 1-99.");

            // Open port comm
            this.port.Open();
            port.BaseStream.Write(Common.MessageLeadIn, 0, Common.MessageLeadIn.Length);    // Lead-in
            port.BaseStream.Write(Encoding.ASCII.GetBytes(memory.ToString("D2")), 0, 2);                // Memory index
            message.WriteTo(port.BaseStream);                                                           // Message itself
            port.BaseStream.Write(Common.MessageLeadOut, 0, Common.MessageLeadOut.Length);  // Lead-out
            this.port.Close();
        }

        /// <summary>
        /// Sets the real time clock in LED panel to current time.
        /// </summary>
        public void SetTime() {
            this.SetTime(DateTime.Now);
        }

        /// <summary>
        /// Sets the real time clock in LED panel to specified time.
        /// </summary>
        /// <param name="time">The time to set.</param>
        public void SetTime(DateTime time) {
            if (this.disposed) throw new ObjectDisposedException("ControlClient");
            this.port.Open();
            port.BaseStream.Write(Common.SetTimeLeadIn, 0, Common.SetTimeLeadIn.Length);    // Lead-in
            port.BaseStream.Write(Encoding.ASCII.GetBytes(time.ToString("yyMMddHHmmss")), 0, 12);       // Specified time
            port.BaseStream.Write(Common.SetTimeLeadOut, 0, Common.SetTimeLeadOut.Length);  // Lead-out
            this.port.Close();
        }

        /// <summary>
        /// Enables the hourly alarm.
        /// </summary>
        public void HourlyAlarmEnable() {
            if (this.disposed) throw new ObjectDisposedException("ControlClient");
            this.port.Open();
            port.BaseStream.Write(Common.SetAlarmOn, 0, Common.SetAlarmOn.Length);
            this.port.Close();
        }

        /// <summary>
        /// Disables the hourly alarm
        /// </summary>
        public void HourlyAlarmDisable() {
            if (this.disposed) throw new ObjectDisposedException("ControlClient");
            this.port.Open();
            port.BaseStream.Write(Common.SetAlarmOff, 0, Common.SetAlarmOff.Length);
            this.port.Close();
        }

        /// <summary>
        /// Runs the self test.
        /// </summary>
        public void RunSelfTest() {
            if (this.disposed) throw new ObjectDisposedException("ControlClient");
            this.port.Open();
            port.BaseStream.Write(Common.SelfTest, 0, Common.SelfTest.Length);
            this.port.Close();        
        }

        #region IDisposable Members

        private bool disposed = false;

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose() {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        private void Dispose(bool disposing) {
            if (this.disposed) return;
            if (disposing) {
                this.port.Dispose();
            }
            this.disposed = true;
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="ControlClient"/> is reclaimed by garbage collection.
        /// </summary>
        ~ControlClient() {
            Dispose(false);
        }

        #endregion
    }
}
